using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

namespace Hsenl.Network {
    public abstract class IOCPReceiver : EpibolySocket, IIOCP, IReceiver {
        public int TcpRecvBufferSizePer { get; set; } = 1024 * 4;
        public int UdpRecvBufferSizePer { get; set; } = 1500;

        private readonly SocketAsyncEventArgs _recvEventArgs = new();

        private volatile int _isRecving;
        private SocketBufferKind _bufferKind; // 有时候如果对于memory支持不友好的环境时, 可以使用array. 例如unity中, 对于memory的支持就不友好, 所以这只是权宜之计, 才增加了对byte[]的支持.

        public bool IsRecving => this._isRecving != 0;
        public SocketBufferKind BufferKind => this._bufferKind;

        protected IOCPReceiver(Socket socket, SocketBufferKind bufferKind) : base(socket) {
            this._recvEventArgs.Completed += this.RecvEventArgs_OnCompleted;
            // 随便设置一个无效端点, 不空就行, 后续接收到数据时, 该值会被系统替换为目标端点
            this._recvEventArgs.Completed += this.RecvFromEventArgs_Completed;
            this._recvEventArgs.Completed += this.RecvMessageFromEventArgs_Completed;
            this.Init(socket, bufferKind);
        }

        public void Init(Socket socket, SocketBufferKind bufferKind) {
            base.Init(socket);
            this._bufferKind = bufferKind;
            this._recvEventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
        }

        // 以下几种接收方案都能接收到数据, 不局限于发送方使用的是那种发送方案
        protected override bool RecvAsync() {
            this.CheckDisposedException();

            if (this.CompareExchange()) {
                this.ReceiveAsync(this._recvEventArgs);
                return true;
            }

            return false;
        }

        // 接收的同时, 能够拿到对方的地址
        protected override bool RecvFromAsync() {
            this.CheckDisposedException();

            if (this.CompareExchange()) {
                this.ReceiveFromAsync(this._recvEventArgs);
                return true;
            }

            return false;
        }

        // 不仅能拿到地址, 还能拿到IP头部信息和SocketFlags
        protected override bool RecvMessageFromAsync() {
            this.CheckDisposedException();

            if (this.CompareExchange()) {
                this.ReceiveMessageFromAsync(this._recvEventArgs);
                return true;
            }

            return false;
        }

        // 接收数据
        private void ReceiveAsync(SocketAsyncEventArgs e) {
            while (true) {
                if (this.IsClosedOrDisposed()) {
                    this.StopRecving();
                    return;
                }

                try {
                    switch (this._bufferKind) {
                        case SocketBufferKind.Memory:
                            var memory = this.GetRecvBuffer(this.TcpRecvBufferSizePer);
                            if (memory.Length == 0) {
                                this.StopRecving();
                                return;
                            }

                            this.CheckGreaterThanTcpRecvSizeException(memory.Length);
                            e.SetBuffer(memory);
                            break;
                        case SocketBufferKind.Array:
                            var bytes = this.GetRecvBuffer(this.TcpRecvBufferSizePer, out int offset, out int count);
                            if (bytes.Length == 0) {
                                this.StopRecving();
                                return;
                            }

                            this.CheckGreaterThanTcpRecvSizeException(count);
                            e.SetBuffer(bytes, offset, count);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    if (!this.Socket.ReceiveAsync(e)) {
                        this.ProcessRecv(e);
                    }
                    else {
                        break;
                    }
                }
                catch (SocketException exception) {
                    this.StopRecving();
                    this.Error(exception.ErrorCode);
                }
                catch {
                    this.StopRecving();
                    throw;
                }
            }
        }

        private void RecvEventArgs_OnCompleted(object sender, SocketAsyncEventArgs e) {
            if (e.LastOperation != SocketAsyncOperation.Receive)
                return;

            this.ProcessRecv(e);
            
            if (e.SocketError == SocketError.Success) {
                if (e.BytesTransferred != 0) {
                    this.ReceiveAsync(e);
                }
                else {
                    // 在 Tcp Recv 时 BytesTransferred == 0 是判断远程断开的唯一方式, 表示优雅断开, 所以 SocketError == Success
                    // 而 SocketError 如果不是 Success, 都认为是连接已断
                    this.StopRecving();
                    this.Error(ErrorCode.ConnectionAborted);
                }
            }
            else {
                this.StopRecving();
                this.Error((int)e.SocketError);
            }
        }

        private void ProcessRecv(SocketAsyncEventArgs e) {
            if (e.SocketError == SocketError.Success) {
                // 当关闭socket的时候, 假如有未处理完的异步操作, 则socket会立即执行一次回调, 把这次回调排除在外
                // 由于回调来自不同的线程, 所以仅靠赋值为空等判断方式并不能保证一定准确, 而如果加锁的话, 又会影响EventArgs的接收效率, 所以使用BytesTransferred判断
                // 是一种较为稳妥的方式
                if (e.BytesTransferred != 0) {
                    switch (this._bufferKind) {
                        case SocketBufferKind.Memory:
                            var data = e.MemoryBuffer.Slice(0, e.BytesTransferred);
                            try {
                                this.OnRecv(data);
                            }
                            catch (Exception exception) {
                                Log.Error(exception);
                            }

                            break;
                        case SocketBufferKind.Array:
                            try {
                                this.OnRecv(e.Buffer, e.Offset, e.BytesTransferred);
                            }
                            catch (Exception exception) {
                                Log.Error(exception);
                            }

                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }
        }

        // 接收数据from
        private void ReceiveFromAsync(SocketAsyncEventArgs e) {
            while (true) {
                if (this.IsClosedOrDisposed()) {
                    this.StopRecving();
                    return;
                }

                try {
                    switch (this._bufferKind) {
                        case SocketBufferKind.Memory:
                            var memory = this.GetRecvBuffer(this.UdpRecvBufferSizePer);
                            if (memory.Length == 0) {
                                this.StopRecving();
                                return;
                            }

                            this.CheckGreaterThanUdpRecvSizeException(memory.Length);
                            e.SetBuffer(memory);
                            break;
                        case SocketBufferKind.Array:
                            var bytes = this.GetRecvBuffer(this.UdpRecvBufferSizePer, out int offset, out int count);
                            if (bytes.Length == 0) {
                                this.StopRecving();
                                return;
                            }

                            this.CheckGreaterThanUdpRecvSizeException(count);
                            e.SetBuffer(bytes, offset, count);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    if (!this.Socket.ReceiveFromAsync(e)) {
                        this.ProcessRecvFrom(e);
                    }
                    else {
                        break;
                    }
                }
                catch (SocketException exception) {
                    this.StopRecving();
                    this.Error(exception.ErrorCode);
                }
                catch {
                    this.StopRecving();
                    throw;
                }
            }
        }

        private void RecvFromEventArgs_Completed(object sender, SocketAsyncEventArgs e) {
            if (e.LastOperation != SocketAsyncOperation.ReceiveFrom)
                return;

            this.ProcessRecvFrom(e);

            if (e.SocketError == SocketError.Success) {
                if (e.BytesTransferred != 0) {
                    this.ReceiveFromAsync(e);
                }
            }
            else {
                this.StopRecving();
                this.Error((int)e.SocketError);
            }

            // Udp协议无法通过 BytesTransferred 来判断是否优雅断开 (因为Udp从未建立过连接), 只能通过自行实现心跳包, 通过超时来判断
        }

        private void ProcessRecvFrom(SocketAsyncEventArgs e) {
            if (e.SocketError == SocketError.Success) {
                if (e.BytesTransferred != 0) {
                    switch (this._bufferKind) {
                        case SocketBufferKind.Memory:
                            var data = e.MemoryBuffer.Slice(0, e.BytesTransferred);
                            try {
                                this.OnRecvFrom(e.RemoteEndPoint, data);
                            }
                            catch (Exception exception) {
                                Log.Error(exception);
                            }

                            break;
                        case SocketBufferKind.Array:
                            try {
                                this.OnRecvFrom(e.RemoteEndPoint, e.Buffer, e.Offset, e.BytesTransferred);
                            }
                            catch (Exception exception) {
                                Log.Error(exception);
                            }

                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }
        }

        // 接收数据message from
        private void ReceiveMessageFromAsync(SocketAsyncEventArgs e) {
            while (true) {
                if (this.IsClosedOrDisposed()) {
                    this.StopRecving();
                    return;
                }

                try {
                    switch (this._bufferKind) {
                        case SocketBufferKind.Memory:
                            var memory = this.GetRecvBuffer(this.UdpRecvBufferSizePer);
                            if (memory.Length == 0) {
                                this.StopRecving();
                                return;
                            }

                            this.CheckGreaterThanUdpRecvSizeException(memory.Length);
                            e.SetBuffer(memory);
                            break;
                        case SocketBufferKind.Array:
                            var bytes = this.GetRecvBuffer(this.UdpRecvBufferSizePer, out int offset, out int count);
                            if (bytes.Length == 0) {
                                this.StopRecving();
                                return;
                            }

                            this.CheckGreaterThanUdpRecvSizeException(count);
                            e.SetBuffer(bytes, offset, count);
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }

                    if (!this.Socket.ReceiveMessageFromAsync(e)) {
                        this.ProcessRecvMessageFrom(e);
                    }
                    else {
                        break;
                    }
                }
                catch (SocketException exception) {
                    this.StopRecving();
                    this.Error(exception.ErrorCode);
                }
                catch {
                    this.StopRecving();
                    throw;
                }
            }
        }

        private void RecvMessageFromEventArgs_Completed(object sender, SocketAsyncEventArgs e) {
            if (e.LastOperation != SocketAsyncOperation.ReceiveMessageFrom)
                return;

            this.ProcessRecvMessageFrom(e);

            if (e.SocketError == SocketError.Success) {
                if (e.BytesTransferred != 0) {
                    this.ReceiveMessageFromAsync(e);
                }
            }
            else {
                this.StopRecving();
                this.Error((int)e.SocketError);
            }
        }

        private void ProcessRecvMessageFrom(SocketAsyncEventArgs e) {
            if (e.SocketError == SocketError.Success) {
                if (e.BytesTransferred != 0) {
                    switch (this._bufferKind) {
                        case SocketBufferKind.Memory:
                            var data = e.MemoryBuffer.Slice(0, e.BytesTransferred);
                            try {
                                this.OnRecvMessageFrom(e.RemoteEndPoint, e.ReceiveMessageFromPacketInfo, e.SocketFlags, data);
                            }
                            catch (Exception exception) {
                                Log.Error(exception);
                            }

                            break;
                        case SocketBufferKind.Array:
                            try {
                                this.OnRecvMessageFrom(e.RemoteEndPoint, e.ReceiveMessageFromPacketInfo, e.SocketFlags, e.Buffer, e.Offset, e.BytesTransferred);
                            }
                            catch (Exception exception) {
                                Log.Error(exception);
                            }

                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            }
        }

        protected abstract Memory<byte> GetRecvBuffer(int length);
        protected abstract byte[] GetRecvBuffer(int length, out int offset, out int count);

        protected virtual void OnRecv(Memory<byte> data) { }
        protected virtual void OnRecv(byte[] data, int offset, int count) { }

        protected virtual void OnRecvFrom(EndPoint remoteEndPoint, Memory<byte> data) { }
        protected virtual void OnRecvFrom(EndPoint remoteEndPoint, byte[] data, int offset, int count) { }

        protected virtual void OnRecvMessageFrom(EndPoint remoteEndPoint, IPPacketInformation information, SocketFlags socketFlags, Memory<byte> data) { }

        protected virtual void OnRecvMessageFrom(EndPoint remoteEndPoint, IPPacketInformation information, SocketFlags socketFlags, byte[] data, int offset,
            int count) { }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private bool CompareExchange() {
            return Interlocked.CompareExchange(ref this._isRecving, 1, 0) == 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void StopRecving() {
            Interlocked.Exchange(ref this._isRecving, 0);
            // 我们无法直接的关闭 saea, 需要等到他 completed, 才算是关闭了
            if (this.IsClosed) {
                this.OnClosed();
            }
        }

        public override void Close(bool closeSocket = true) {
            if (this.IsClosed)
                return;

            base.Close(closeSocket);

            // 清空 saea, 以供复用
            this._recvEventArgs.RemoteEndPoint = null;
            this._recvEventArgs.UserToken = null;

            if (!this.IsRecving) {
                this.OnClosed();
            }
        }

        public override void Dispose() {
            if (this.IsDisposed)
                return;

            base.Dispose();

            this._recvEventArgs.Completed -= this.RecvEventArgs_OnCompleted;
            this._recvEventArgs.Completed -= this.RecvFromEventArgs_Completed;
            this._recvEventArgs.Completed -= this.RecvMessageFromEventArgs_Completed;
            this._recvEventArgs.RemoteEndPoint = null;
            this._recvEventArgs.UserToken = null;
            this._recvEventArgs.Dispose();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private bool IsClosedOrDisposed() {
            return this.IsClosed || this.IsDisposed;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void CheckGreaterThanTcpRecvSizeException(int len) {
            if (len > this.TcpRecvBufferSizePer)
                throw new ArgumentOutOfRangeException($"RecvBufferLength over maximum '{len}' '{this.TcpRecvBufferSizePer}'");
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private void CheckGreaterThanUdpRecvSizeException(int len) {
            if (len > this.UdpRecvBufferSizePer)
                throw new ArgumentOutOfRangeException($"RecvBufferLength over maximum '{len}' '{this.UdpRecvBufferSizePer}'");
        }
    }
}